﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections;
using System.Windows;
using System.Windows.Data;

namespace Skywave.Reflection
{
    public class Evaluator
    {
        private static Regex regex_Members = new Regex(@"(?<g1>[^.]+)[.]*");
        private static Regex regex_IndexedProp = new Regex(@"(?<g1>[^\[]+)(?<g2>[\[](?<g21>[^\]]+)[\]])+");
        /// <summary>
        /// This version of evaluating will use reflection classes and regular expressions (coded by Skywave) to retrieve the value from the source.
        /// </summary>
        /// <param name="source">The source object containing the data.</param>
        /// <param name="path">The path to value in the source object.</param>
        /// <returns>The value retrieved from the source using the path provided.</returns>
        public static object Evaluate(object source, string path)
        {
            object r = null;
            if (source == null)
                return r;
            try
            {
                Type ty1 = source.GetType();
                Type tyDictionary = typeof(IDictionary);
                MemberInfo[] memInfos1;
                MemberInfo memInfo1;
                PropertyInfo propInfo1;
                FieldInfo fieldInfo1;
                Match match_IndexedProp;
                string subPath;
                object subIndex;
                string subIndex_Text;
                r = source;
                if (!string.IsNullOrEmpty(path) && path != "this")
                {
                    foreach (Match fe1 in regex_Members.Matches(path))
                    {
                        subPath = fe1.Groups["g1"].Value;
                        if (subPath == "this")
                            continue;
                        match_IndexedProp = regex_IndexedProp.Match(subPath);
                        if (match_IndexedProp != null && match_IndexedProp.Success)
                        {
                            memInfos1 = ty1.GetMember(match_IndexedProp.Groups["g1"].Value);
                            if (memInfos1.Length > 0)
                            {
                                memInfo1 = memInfos1[0];
                                for (int iIndexed = 0; iIndexed < match_IndexedProp.Groups["g21"].Captures.Count; iIndexed++)
                                {
                                    subIndex_Text = match_IndexedProp.Groups["g21"].Captures[iIndexed].Value;
                                    //
                                    if (StringHelper.IsAllDigit(subIndex_Text))
                                        subIndex = int.Parse(subIndex_Text);
                                    else
                                    {
                                        if (subIndex_Text.StartsWith("\""))
                                            subIndex = subIndex_Text.Trim(new char[] { '"' });
                                        else
                                            subIndex = subIndex_Text;
                                    }
                                    //
                                    if (iIndexed == 0)
                                    {
                                        if (memInfo1.MemberType == MemberTypes.Property)
                                        {
                                            propInfo1 = memInfo1 as PropertyInfo;
                                            if (tyDictionary.IsAssignableFrom(propInfo1.PropertyType))
                                                r = (propInfo1.GetValue(r, null) as IDictionary)[subIndex];
                                        }
                                        else if (memInfo1.MemberType == MemberTypes.Field)
                                        {
                                            fieldInfo1 = memInfo1 as FieldInfo;
                                            if (tyDictionary.IsAssignableFrom(fieldInfo1.FieldType))
                                                r = (fieldInfo1.GetValue(r) as IDictionary)[subIndex];
                                        }
                                        else
                                            r = null;
                                    }
                                    else
                                        r = (r as IDictionary)[subIndex];
                                    //
                                    if (r == null)
                                        break;
                                }
                            }
                        }
                        else
                        {
                            memInfos1 = ty1.GetMember(subPath);
                            if (memInfos1.Length > 0)
                            {
                                memInfo1 = memInfos1[0];
                                if (memInfo1.MemberType == MemberTypes.Property)
                                    r = ty1.GetProperty(subPath).GetValue(r, null);
                                else if (memInfo1.MemberType == MemberTypes.Field)
                                    r = ty1.GetField(subPath).GetValue(r);
                                else
                                    r = null;
                            }
                            else
                                r = null;
                        }
                        if (r == null)
                            break;
                        else
                        {
                            ty1 = r.GetType();
                        }
                    }
                    if (r == source)
                        r = null;
                }
            }
            catch (Exception ex1)
            {
                System.Diagnostics.Debug.WriteLine(ex1.ToString());
                r = null;
            }
            return r;
        }

        private static readonly DependencyProperty DummyProperty = DependencyProperty.RegisterAttached(
        "Dummy",
        typeof(Object),
        typeof(DependencyObject),
        new UIPropertyMetadata(null));

        /// <summary>
        /// This version of evaluating will use WPF binding hack (coded by Marco Zhou) to retrieve the value from the source.
        /// </summary>
        /// <param name="container">The source object containing the data.</param>
        /// <param name="expression">The binding path to value in the source object.</param>
        /// <returns>The value retrieved from the source using the binding path provided.</returns>
        public static Object Eval(Object container, String expression)
        {
            Binding binding = new Binding(expression) { Source = container };
            DependencyObject dummyDO = new DependencyObject();
            BindingOperations.SetBinding(dummyDO, DummyProperty, binding);
            return dummyDO.GetValue(DummyProperty);
        }
    }
}
